package com.example.educationblockchain.util;


import com.alibaba.fastjson.JSON;
import com.example.educationblockchain.model.Block;
import com.example.educationblockchain.model.Data;
import com.github.luben.zstd.Zstd;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Encrypted {

    static{
        try{
            Security.addProvider(new BouncyCastleProvider());
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private static void printByte(byte[] array,String s){
        System.out.println(s+":");
        for(byte i:array){
            System.out.print(i+" ");
        };
        System.out.println();
    }

    public Map<String,String> Encrypt(Data data) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeyException {
        Map<String,String> result = new HashMap<>() ;


        String m = JSON.toJSONString(data);

        byte[] M = m.getBytes("UTF-8");
        int proportion = 12;
        int p = M.length/proportion;
        byte[] m1 = Arrays.copyOfRange(M,p,M.length);
        byte[] m2 = Arrays.copyOf(M,p);

        byte[] m1_1 = Zstd.compress(m1,1);

        SecretKey secretKey;
        secretKey = Aes.generateKey();
        byte[] sk1 = secretKey.getEncoded();
        byte[] c1 = Aes.encrypt(m1_1,secretKey);

        byte[] c2 = XORCrypto.encrypt(c1,sk1);



        KeyPair keyPair = ECC.generateECCKeyPair(256);
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] sk1_1 = ECC.eccEncrypt(publicKey,sk1);
        byte[] c3 = ECC.eccEncrypt(publicKey,m2);

        byte[] c4 = XORCrypto.encrypt(c3,sk1_1);
        byte[] c5 = XORCrypto.encrypt(c2,c4);

        result.put("c4",JSON.toJSONString(c4));
        result.put("c5",JSON.toJSONString(c5));
        result.put("sk",JSON.toJSONString(sk1_1));
        result.put("pk",JSON.toJSONString(privateKey.getEncoded()));

        return result;
    }

    public Data Decrypt(Block block,String C5) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidKeyException {

        byte[] c5 = java.util.Base64.getMimeDecoder().decode(C5);
        byte[] c4 = java.util.Base64.getMimeDecoder().decode(block.getC4());

        KeyFactory kf = KeyFactory.getInstance("EC","BC"); // or "EC" or whatever

        byte[] priKey_ = java.util.Base64.getMimeDecoder().decode(block.getPk());

        PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(priKey_));

        byte[] sk1_1 = java.util.Base64.getMimeDecoder().decode(block.getSk());

        byte[] C2 = XORCrypto.encrypt(c5,c4);
        byte[] Sk1 = ECC.eccDecrypt(privateKey,sk1_1);
        SecretKey originalKey_1 = new SecretKeySpec(Sk1, 0, Sk1.length, "AES");
        byte[] C1 = XORCrypto.encrypt(C2,Sk1);
        byte[] M1_1 = Aes.decrypt(C1,originalKey_1);

        int size = (int) Zstd.decompressedSize(M1_1);
        byte[] M1 = new byte[size];
        Zstd.decompress(M1, M1_1);

        byte[] C3 = XORCrypto.encrypt(c4,sk1_1);
        byte[] M2 = ECC.eccDecrypt(privateKey, C3);

        byte[] MM = new byte[M1.length + M2.length];
        System.arraycopy(M2, 0, MM, 0, M2.length);
        System.arraycopy(M1, 0, MM, M2.length, M1.length);

        Data data = JSON.parseObject(MM,Data.class);

        return data;
    }

    public static String SHA256(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (Exception e) {
            System.out.println("getSHA256 is error" + e.getMessage());
        }
        return encodeStr;
    }

    private static String byte2Hex(byte[] bytes) {
        StringBuilder builder = new StringBuilder();
        String temp;
        for (int i = 0; i < bytes.length; i++) {
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
                builder.append("0");
            }
            builder.append(temp);
        }
        return builder.toString();
    }

    public static String getRandomString(int length){
        String str="zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0; i<length; ++i){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }
}
